package org.luxor.commons.storage.service.impl;

import org.luxor.commons.core.utils.HttpContextUtils;
import org.luxor.commons.core.utils.StrKit;
import org.luxor.commons.core.utils.StringUtils;
import org.luxor.commons.storage.client.AbstractStorageClient;
import org.luxor.commons.storage.client.AbstractStorageProperties;
import org.luxor.commons.storage.client.AntiStealToken;
import org.luxor.commons.storage.client.aliyun.AliyunStorageClient;
import org.luxor.commons.storage.client.disk.DiskStorageClient;
import org.luxor.commons.storage.client.fastdfs.FastDfsStorageClient;
import org.luxor.commons.storage.client.minio.MinioStorageClient;
import org.luxor.commons.storage.client.qcloud.QcloudStorageClient;
import org.luxor.commons.storage.client.qiniu.QiniuStorageClient;
import org.luxor.commons.storage.config.HttpConfig;
import org.luxor.commons.storage.config.ThumbImageConfig;
import org.luxor.commons.storage.constant.StorageType;
import org.luxor.commons.storage.exception.AccessDeniedException;
import org.luxor.commons.storage.exception.StorageServiceException;
import org.luxor.commons.storage.exception.StorageTypeNotSupportedException;
import org.luxor.commons.storage.exception.ThumbnailsNotSupportedException;
import org.luxor.commons.storage.service.IFileStorageService;
import org.luxor.commons.storage.service.IThumbImageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Instant;

/**
 * OSS文件存储服务
 *
 * @author Mr.Yan  @date 2019/12/20
 */
@Service
public class FileStorageServiceImpl implements IFileStorageService {

    private static final Logger logger = LoggerFactory.getLogger(FileStorageServiceImpl.class);

    @Resource
    private DiskStorageClient diskStorageClient;
    @Resource
    private MinioStorageClient minioStorageClient;
    @Resource
    private FastDfsStorageClient fastDfsStorageClient;
    @Resource
    private QiniuStorageClient qiniuStorageClient;
    @Resource
    private AliyunStorageClient aliyunStorageClient;
    @Resource
    private QcloudStorageClient qcloudStorageClient;
    @Resource
    private IThumbImageService thumbImageService;

    /**
     * 存储方式选择器
     *
     * @param storageType (1：本地磁盘 2：FastDfs 3：七牛  4：阿里云  5：腾讯云)
     * @return StorageClient
     */
    private AbstractStorageClient build(StorageType storageType) throws StorageTypeNotSupportedException {
        switch (storageType) {
            case DISK:
                return diskStorageClient;
            case MINIO:
                return minioStorageClient;
            case FAST_DFS:
                return fastDfsStorageClient;
            case QINIU:
                return qiniuStorageClient;
            case ALIYUN:
                return aliyunStorageClient;
            case QCLOUD:
                return qcloudStorageClient;
            default:
                logger.error("存储类型(storageType):[{}],目前暂不支持", storageType);
                throw new StorageTypeNotSupportedException("存储类型(storageType):[" + storageType + "],目前暂不支持");
        }
    }

    @Override
    public String getFileUrl(StorageType storageType, String fileFullPath) {
        HttpConfig httpConfig = build(storageType).getConfig().getHttp();
        return httpConfig.getResAccessUrl(fileFullPath);
    }

    @Override
    public String upload(StorageType storageType, byte[] fileByte, String fileName, String fileSuffix) throws StorageServiceException {
        return build(storageType).upload(fileByte, fileName, fileSuffix);
    }

    @Override
    public byte[] download(StorageType storageType, String fileFullPath) throws StorageServiceException {
        return build(storageType).download(fileFullPath);
    }

    @Override
    public void httpDownload(StorageType storageType, String fileFullPath, HttpServletResponse response) throws StorageServiceException {
        AbstractStorageProperties properties = build(storageType).getConfig();
        HttpConfig httpConfig = properties.getHttp();
        if (httpConfig.getAntiStealToken()) {
            try {
                long ts = Instant.now().getEpochSecond();
                String secretKey = httpConfig.getSecretKey();
                String token = AntiStealToken.getToken(fileFullPath, secretKey, httpConfig.getCharset(), ts);
                String redirectUrl = httpConfig.getResAccessUrl(fileFullPath) + "?token=" + token + "&ts=" + ts;
                response.sendRedirect(redirectUrl);
                return;
            } catch (IOException e) {
                logger.error("sendRedirect失败!", e);
                throw new StorageServiceException("sendRedirect失败!", e);
            }
        }
        byte[] fileByte = download(storageType, fileFullPath);
        HttpContextUtils.write(fileByte, response);
    }

    @Override
    public void antiStealHttpDownload(StorageType storageType, String fileFullPath, String token, Long ts, HttpServletResponse response) throws StorageServiceException {
        HttpConfig http = build(storageType).getConfig().getHttp();
        if (http.getAntiStealToken()) {
            String secretKey = http.getSecretKey();
            String charset = http.getCharset();
            long ttl = http.getTtl();
            boolean verifyResult = AntiStealToken.verify(token, ttl, fileFullPath, secretKey, charset, ts);
            if (verifyResult) {
                byte[] fileByte = download(storageType, fileFullPath);
                HttpContextUtils.write(fileByte, response);
            } else {
                logger.warn("AccessDenied (AntiStealToken is not valid)");
                throw new AccessDeniedException("AccessDenied (AntiStealToken is not valid)");
            }
            return;
        }
        byte[] fileByte = download(storageType, fileFullPath);
        HttpContextUtils.write(fileByte, response);
    }

    @Override
    public void delete(StorageType storageType, String fileFullPath) throws StorageServiceException {
        build(storageType).delete(fileFullPath);
    }

    @Override
    public String uploadImageAndCrtThumbImage(StorageType storageType, byte[] imageByte, String imageName, String imageSuffix, int width, int height) throws StorageServiceException {
        if (StringUtils.isBlank(imageName)) {
            imageName = StrKit.NONE;
        }
        AbstractStorageProperties properties = build(storageType).getConfig();
        ThumbImageConfig thumbImageConfig = properties.getThumbImage();
        boolean isSupport = thumbImageConfig.isSupportImage(imageSuffix);
        if (isSupport) {
            if (width == 0) {
                width = thumbImageConfig.getWidth();
            }
            if (height == 0) {
                height = thumbImageConfig.getHeight();
            }
            byte[] thumbImage = thumbImageService.getThumbImage(imageByte, width, height);
            String fileName = imageName + "!" + width + "x" + height;
            return upload(storageType, thumbImage, fileName, imageSuffix);
        }
        logger.warn("文件类型:[{}],不支持生成缩略图", imageSuffix);
        throw new ThumbnailsNotSupportedException("文件类型:[" + imageSuffix + "],不支持生成缩略图");
    }
}
